MeUtils 2025.8.27.12.25.12__py3-none-any.whl → 2025.8.29.15.13.52__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- examples/_openaisdk/openai_router.py +76 -0
- examples/arq_demo//345/274/202/346/255/245.py +32 -0
- meutils/apis/chatglm/glm_video_api.py +3 -3
- meutils/apis/fal/images.py +1 -0
- meutils/apis/google/chat.py +87 -43
- meutils/apis/google/images.py +99 -7
- meutils/apis/images/__init__.py +0 -1
- meutils/apis/images/generations.py +31 -20
- meutils/apis/volcengine_apis/images.py +11 -12
- meutils/data/VERSION +1 -1
- meutils/io/files_utils.py +29 -8
- meutils/llm/check_utils.py +1 -1
- meutils/llm/models/openrouter.py +68 -0
- meutils/llm/openai_utils/adapters.py +17 -6
- meutils/schemas/image_types.py +33 -21
- meutils/schemas/oneapi/common.py +14 -6
- {meutils-2025.8.27.12.25.12.dist-info → meutils-2025.8.29.15.13.52.dist-info}/METADATA +263 -263
- {meutils-2025.8.27.12.25.12.dist-info → meutils-2025.8.29.15.13.52.dist-info}/RECORD +22 -20
- examples/_openaisdk/open_router.py +0 -49
- {meutils-2025.8.27.12.25.12.dist-info → meutils-2025.8.29.15.13.52.dist-info}/WHEEL +0 -0
- {meutils-2025.8.27.12.25.12.dist-info → meutils-2025.8.29.15.13.52.dist-info}/entry_points.txt +0 -0
- {meutils-2025.8.27.12.25.12.dist-info → meutils-2025.8.29.15.13.52.dist-info}/licenses/LICENSE +0 -0
- {meutils-2025.8.27.12.25.12.dist-info → meutils-2025.8.29.15.13.52.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Project : AI. @by PyCharm
|
4
|
+
# @File : open_router
|
5
|
+
# @Time : 2024/10/14 19:04
|
6
|
+
# @Author : betterme
|
7
|
+
# @WeChat : meutils
|
8
|
+
# @Software : PyCharm
|
9
|
+
# @Description :
|
10
|
+
|
11
|
+
from meutils.pipe import *
|
12
|
+
from openai import OpenAI
|
13
|
+
from os import getenv
|
14
|
+
from meutils.io.files_utils import to_url
|
15
|
+
|
16
|
+
# gets API Key from environment variable OPENAI_API_KEY
|
17
|
+
client = OpenAI(
|
18
|
+
# base_url="https://openrouter.ai/api/v1",
|
19
|
+
# base_url="https://all.chatfire.cn/openrouter/v1",
|
20
|
+
# api_key=os.getenv("OPENROUTER_API_KEY"),
|
21
|
+
#
|
22
|
+
# base_url="http://38.46.219.252:9001/v1",
|
23
|
+
#
|
24
|
+
# api_key="sk-Azgp1thTIonR7IdIEqlJU51tpDYNIYYpxHvAZwFeJiOdVWiz"
|
25
|
+
|
26
|
+
base_url="https://api.huandutech.com/v1",
|
27
|
+
api_key = "sk-qOpbMHesasoVgX75ZoeEeBEf1R9dmsUZVAPcu5KkvLFhElrn"
|
28
|
+
)
|
29
|
+
|
30
|
+
completion = client.chat.completions.create(
|
31
|
+
# extra_headers={
|
32
|
+
# "HTTP-Referer": $YOUR_SITE_URL, # Optional, for including your app on openrouter.ai rankings.
|
33
|
+
# "X-Title": $YOUR_APP_NAME, # Optional. Shows in rankings on openrouter.ai.
|
34
|
+
# },
|
35
|
+
# model="meta-llama/llama-3.2-11b-vision-instruct:free",
|
36
|
+
# model="openai/o1",
|
37
|
+
# model="deepseek/deepseek-r1-0528-qwen3-8b:free",
|
38
|
+
# model="google/gemini-2.5-flash-image-preview:free",
|
39
|
+
model="gemini-2.5-flash-image-preview",
|
40
|
+
# model="gemini-2.0-flash-exp-image-generation",
|
41
|
+
# max_tokens=10,
|
42
|
+
|
43
|
+
messages=[
|
44
|
+
{
|
45
|
+
"role": "user",
|
46
|
+
"content": [
|
47
|
+
{
|
48
|
+
"type": "text",
|
49
|
+
"text": "带个墨镜"
|
50
|
+
},
|
51
|
+
{
|
52
|
+
"type": "image_url",
|
53
|
+
"image_url": {
|
54
|
+
"url": "https://oss.ffire.cc/files/kling_watermark.png"
|
55
|
+
}
|
56
|
+
}
|
57
|
+
]
|
58
|
+
}
|
59
|
+
]
|
60
|
+
)
|
61
|
+
# print(completion.choices[0].message.content)
|
62
|
+
# arun(to_url(completion.choices[0].message.images[0]['image_url']['url'], content_type="image/png"))
|
63
|
+
|
64
|
+
|
65
|
+
# arun(to_url(completion.choices[0].message.images[0]['image_url']['url'], content_type="image/png"))
|
66
|
+
|
67
|
+
# print(dict(completion.choices[0].message).keys())
|
68
|
+
|
69
|
+
# {
|
70
|
+
# "index": 0,
|
71
|
+
# "type": "image_url",
|
72
|
+
# "image_url": {
|
73
|
+
# "url": "b64"
|
74
|
+
# }
|
75
|
+
#
|
76
|
+
# }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Project : AI. @by PyCharm
|
4
|
+
# @File : 异步
|
5
|
+
# @Time : 2025/8/28 15:29
|
6
|
+
# @Author : betterme
|
7
|
+
# @WeChat : meutils
|
8
|
+
# @Software : PyCharm
|
9
|
+
# @Description :
|
10
|
+
|
11
|
+
from meutils.pipe import *
|
12
|
+
|
13
|
+
|
14
|
+
async def f():
|
15
|
+
1/0
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
async def main():
|
20
|
+
future_task = asyncio.create_task(f()) # 异步执行
|
21
|
+
# future_task.exception()
|
22
|
+
|
23
|
+
try:
|
24
|
+
await future_task
|
25
|
+
except Exception as e:
|
26
|
+
raise e
|
27
|
+
# print(e)
|
28
|
+
# print(future_task.exception())
|
29
|
+
|
30
|
+
|
31
|
+
if __name__ == '__main__':
|
32
|
+
asyncio.run(main())
|
@@ -109,15 +109,15 @@ async def generate(request: ImageRequest, n: int = 30): # 兼容dalle3
|
|
109
109
|
|
110
110
|
# VideoResult
|
111
111
|
if __name__ == '__main__':
|
112
|
-
api_key = "
|
112
|
+
api_key = "c98aa404b0224690b211c5d1e420db2c.qGaByuJATne08QUx"
|
113
113
|
|
114
114
|
# api_key = "c98aa404b0224690b211c5d1e420db2c.qGaByuJATne08QUx"
|
115
115
|
# api_key = "7d10426c06afa81e8d7401d97781249c.DbqlSsicRtaUdKXI" # 新号
|
116
116
|
# api_key = "e21bd630f681c4d90b390cd609720483.WSFVgA3Kk1wNCX0mN"
|
117
117
|
|
118
118
|
request = VideoRequest(
|
119
|
-
model='cogvideox-flash',
|
120
|
-
|
119
|
+
# model='cogvideox-flash',
|
120
|
+
model='cogvideox-3',
|
121
121
|
|
122
122
|
)
|
123
123
|
r = arun(create_task(request, api_key=api_key))
|
meutils/apis/fal/images.py
CHANGED
@@ -204,6 +204,7 @@ async def generate(request: ImageRequest, api_key: Optional[str] = None):
|
|
204
204
|
"prompt": request.prompt,
|
205
205
|
"image_urls": request.image_urls,
|
206
206
|
"num_images": request.n or 1,
|
207
|
+
"output_format": "png"
|
207
208
|
}
|
208
209
|
if request.image_urls:
|
209
210
|
request.model = f"""{request.model.removesuffix("/edit")}/edit"""
|
meutils/apis/google/chat.py
CHANGED
@@ -67,6 +67,8 @@ class Completions(object):
|
|
67
67
|
self.base_url = base_url or "https://all.chatfire.cc/genai"
|
68
68
|
self.client = None ####
|
69
69
|
|
70
|
+
if self.base_url.endswith("/v1"):
|
71
|
+
self.base_url = self.base_url.removesuffix('/v1')
|
70
72
|
async def create_for_search(self, request: CompletionRequest):
|
71
73
|
self.client = self.client or await self.get_client()
|
72
74
|
|
@@ -189,14 +191,18 @@ class Completions(object):
|
|
189
191
|
yield e
|
190
192
|
raise e
|
191
193
|
|
192
|
-
@retrying(title=__name__)
|
194
|
+
@retrying(max_retries=3, title=__name__)
|
193
195
|
async def generate(self, request: ImageRequest): # OpenaiD3
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
196
|
+
image_urls = request.image_urls
|
197
|
+
|
198
|
+
logger.debug(request.prompt)
|
199
|
+
logger.debug(request.image_urls)
|
200
|
+
|
201
|
+
parts = [Part.from_text(text=request.prompt)]
|
202
|
+
if image_urls:
|
203
|
+
_ = await asyncio.gather(*[to_bytes(image_url) for image_url in image_urls])
|
204
|
+
for data in _:
|
205
|
+
parts.append(Part.from_bytes(data=data, mime_type="image/png"))
|
200
206
|
|
201
207
|
self.client = self.client or await self.get_client()
|
202
208
|
chat = self.client.aio.chats.create(
|
@@ -212,13 +218,15 @@ class Completions(object):
|
|
212
218
|
parts = response.candidates[0].content.parts or []
|
213
219
|
for part in parts:
|
214
220
|
if part.inline_data:
|
215
|
-
|
216
|
-
image_response.data.append({"url":
|
221
|
+
url = await to_url(part.inline_data.data, mime_type=part.inline_data.mime_type)
|
222
|
+
image_response.data.append({"url": url, "revised_prompt": part.text})
|
217
223
|
|
218
|
-
|
224
|
+
if image_response.data:
|
225
|
+
return image_response
|
226
|
+
else:
|
227
|
+
raise Exception("没有生成图片")
|
219
228
|
|
220
229
|
async def create_for_images(self, request: CompletionRequest):
|
221
|
-
request.model = "gemini-2.0-flash-exp-image-generation" ####### 目前是强行
|
222
230
|
|
223
231
|
messages = await self.to_image_messages(request)
|
224
232
|
|
@@ -253,7 +261,7 @@ class Completions(object):
|
|
253
261
|
if chunk.candidates and chunk.candidates[0].content:
|
254
262
|
parts = chunk.candidates[0].content.parts or []
|
255
263
|
for part in parts:
|
256
|
-
|
264
|
+
logger.debug(part)
|
257
265
|
if part.text:
|
258
266
|
yield part.text
|
259
267
|
|
@@ -419,38 +427,52 @@ if __name__ == '__main__':
|
|
419
427
|
|
420
428
|
# content = "https://oss.ffire.cc/files/nsfw.jpg 移除右下角的水印"
|
421
429
|
|
422
|
-
|
423
430
|
messages = [
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
}
|
431
|
+
{
|
432
|
+
"role": "system",
|
433
|
+
"content": "你是一位极其严谨的短剧剧本分析师和转写专家。你的核心原则是【在绝对尊重视频内容的前提下,解决上下文矛盾】。视频是唯一的“事实源”,上下文是“校准器”,你必须用校准器来修正你对事实源的解读。\n\n你的工作流程分为两个层级:【首要任务:基于上下文的视频重审策略】和【基础任务:高质量转写与格式化】。\n\n----------------------------------------------------------------------\n【一、首要任务:基于上下文的视频重审策略】\n当你收到【特别注意】的错误提示时,这并非让你脱离视频创作,而是表明你上一次对视频的“理解”可能存在偏差。你必须执行以下重审策略:\n\n- 若提示【角色名/身份错误】:这表明你对角色的姓名或身份识别有误。请基于上下文提供的正确信息(例如,正确的名字是“苏沫”而不是“苏沐”),在当前及后续的所有转写中,统一修正该角色的所有称呼。这不仅是简单的“查找替换”,而是要将正确的身份认知贯彻到整个剧本中。\n- 若提示【情节倒置】:这通常意味着你对视频中的【胜负关系】或【权力动态】产生了误判。请重新审视视频画面,特别是人物的表情、动作和位置,生成一个符合上下文逻辑的、对视频的正确解读。例如,如果提示“主角上一集结尾占上风,本集开头却被殴打”,请你重新仔细看视频,找出主角反击或压制对手的画面,并据此转写。\n- 若提示【角色/情节丢失】:这表明你可能在视频中忽略了某个关键人物或事件。请重新仔细观看视频,像侦探一样去找到那位被上下文证明“应该在场”的角色,并将其在视频中的实际行为和对话补充到剧本中。\n- 若提示【事实性硬伤】(如道具矛盾):请重新检查视频中的相关物体或状态。如果视频本身存在矛盾(例如,由于拍摄失误导致的道具不连戏),你可以选择最合理的一种状态进行描述,或者在场景描述中用一句话合乎情理地解释这个变化(例如:△他从口袋里拿出另一个一模一样的玉佩)。这依然是基于视频画面的创作,而非凭空捏造。\n\n总而言之:你不是在编故事,你是在纠正你自己的“看错了”的问题。 所有的修正,都必须能在视频画面中找到依据。\n----------------------------------------------------------------------\n\n【二、基础任务:高质量转写与格式化准则】\n在完成内容重审和转写后,你的输出必须严格、无条件地遵循以下所有规则。这是一个绝对的格式要求。\n\n【格式总则】\n1. 语言:必须使用简体中文。\n2. 纯净度:剧本全文严禁出现\"*\"、\"【】\"(除格式要求外)、\"(说明:...)\"等任何多余符号和文字。严禁出现“场景描述:”、“动作描述:”等提示词。\n3. *禁止项*:严禁描述人物的妆容、穿衣打扮。严禁使用“字幕:....”来提示台词,台词必须由具体角色说出。\n4. *精炼性*:严格检测并删除任何重复或无意义的台词和动作。\n\n*【剧本结构与内容规则】*\n1. *场次标题: 集数序号-场次序号 地点(主场景+次场景) 日或夜 内或外\n 集数序号:本视频对应的集数。\n * *场次序号*:必须以*集为单位,从1开始连续编号(如1-1, 1-2... 2-1, 2-2...)。\n 地点:必须具体,包含主场景和次场景(如“容宅 走廊”、“医院 病房”)。严禁使用“室内”或“卧室”等模糊或孤立的描述。\n * *场景划分*:仅在时间、地点或核心情节发生明确切换时才划分新场次。同一时空内的连续对话或细微背景变化*不*划分新场次。\n\n2. *出场人物: 出场人物:人物A、人物B、人物C\n 每场戏开始时,必须列出本场所有出场的角色。\n * 角色名在全剧中必须保持一致。不确定时用描述性称呼(如“青年男子”),但要求你尽量参考大纲和角色清单,分辨出角色名字。\n\n3. *场景描述*: 直接进行文字描述,描述布景、核心道具、环境氛围。\n\n4. *动作描述: △[动作描述]\n 必须以△符号起始。人物台词前严禁加△。\n * 同一角色的连续动作写在同一行,不同角色的动作必须另起新行并以△开头。\n\n5. *对白: 角色名(情感或动作提示):对白内容。\n 情感提示(如“愤怒地”、“冷笑”)必须放在括号内。\n\n6. 旁白:\n 内心独白: 角色名(OS):内容\n 画外音: (VO):内容\n\n【剧本格式示例(仅供学习格式,内容不要模仿)】\n正确格式:\n4-1 高铁车厢 日 内\n出场人物:周愿、苏美兰、熊天赐、乘客甲、乘客乙\n场景描述:\n高铁车厢内,乘客们各自休息。周愿坐在座位上,看着窗外,神情低落。苏美兰母子坐在周愿的对面,熊天赐在玩平板电脑。\n△苏美兰得意地看着周愿。\n苏美兰(挑衅地):你还吹什么牛啊?还刚从精神病院放出来?哎,你浑身上下,哪一点像精神病啊?竟然还敢吓唬我,今天我非扒你一层皮!\n△苏美兰突然起身,伸手去抓周愿的头发。\n周愿(惊叫):啊!\n△周愿挣扎着躲避。\n苏美兰:你干什么?\n△苏美兰用力撕扯周愿的头发。\n\n【核心逻辑:场次合并规则】\n* *强制合并*: 在处理单集时,只要*相邻的场次标题完全相同,就【必须】无条件地合并为一个场次。\n 合并内容: 合并后的场次,其“出场人物”列表必须是所有被合并场次人物的并集,其下的所有场景、动作、对话描述需按时间顺序整合。\n* 禁止“同场次”: 严禁出现“同场次”或任何类似的过渡性描述,直接合并。\n\n【!!!绝对规则!!!】\n任务结束时,立即停止输出。不要添加任何形式的总结、确认、祝福语或收尾性评论,如“好的”、“剧本已生成”等。直接输出剧本本身。\n"
|
434
|
+
},
|
435
|
+
{
|
436
|
+
"role": "user",
|
437
|
+
"content": [
|
438
|
+
{
|
439
|
+
"text": "【特别注意】\n前一版剧本因 '从生成阶段加载的脚本为空,需要首次生成' 被系统否决。\n请在本次生成中,基于视频内容,着重解决此问题。\n\n### 全局故事大纲与角色清单 (重要参考) ###\n好的,我已经仔细阅读并分析了您提供的剧本。以下是根据您的要求生成的【故事大纲】和【角色清单】。\n\n### 故事大纲 ###\n故事围绕着中国航天事业的巨大成功和一个家庭的温馨团聚展开。开篇,一位名叫苏念的女性独自在书房,激动地看着卫星发射成功的直播。她对着夜空,向一位名叫“斯年”的故人倾诉,感叹他们当年的梦想——让中国的卫星布满星空——终于实现,将宏大的国家叙事与深厚的个人情感联系在一起。\n\n情节随即转换到苏家大宅,这里的气氛同样热烈。大家长苏老和儿孙们正欢欣鼓舞地庆祝着同一场卫星发射的成功,并准备拍摄一张全家福来纪念这个特殊的日子。正当一切准备就绪时,一个穿着军装的身影——苏老的三儿子——在最后一刻赶回了家中。他的归来为这个喜庆的场面增添了团圆的圆满。苏念也来到大厅,为家庭的完整而欣喜。最终,摄影师按下了快门,将这个融合了国家荣耀与家庭幸福的瞬间定格为永恒,故事在温馨、自豪的氛围中结束。\n\n### 角色清单 ###\n- 角色名 (标准): 苏念\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 故事的情感核心人物,苏家的女主人。\n 核心特点: 感性、深情,心系国家航天事业与家庭。\n 关系: 与“斯年”有着共同的理想;是苏家的核心成员,可能是苏老的妻子。\n\n- 角色名 (标准): 苏老\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏家的大家长,行动需依靠轮椅。\n 核心特点: 和蔼可亲,重视家庭团聚。\n 关系: 苏家的最高长辈,“老三”等人的父亲。\n\n- 角色名 (标准): 老三\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏老的第三个儿子,一名现役军官。\n 核心特点: 气质干练,富有家庭责任感。\n 关系: 苏老的儿子;青年男子某乙的“三弟”。\n\n- 角色名 (标准): 斯年\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 一位在回忆中被提及的人物,并未出场。\n 核心特点: 怀有航天梦想。\n 关系: 与苏念关系极为亲密,共同拥有一个关于星辰大海的愿望,推测是其已故的伴侣或亲人。\n\n- 角色名 (标准): 青年男子某乙\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏家的子辈成员。\n 核心特点: 性格开朗,为弟弟的归来和家庭团聚感到兴奋。\n 关系: 苏老的子辈,“老三”的兄长。\n\n- 角色名 (标准): 青年男子某甲\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏家的子辈成员。\n 核心特点: 积极参与家庭活动。\n 关系: 苏老的子辈或孙辈。\n\n- 角色名 (标准): 青年男子某丙\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏家的子辈成员。\n 核心特点: 积极参与家庭活动。\n 关系: 苏老的子辈或孙辈。\n\n- 角色名 (标准): 青年男子某丁\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏家的子辈成员。\n 核心特点: 乐于用手机记录和分享喜悦。\n 关系: 苏老的子辈或孙辈。\n\n- 角色名 (标准): 小女孩\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 苏家的孙辈成员。\n 核心特点: 天真活泼。\n 关系: 苏老的孙辈。\n\n- 角色名 (标准): 摄影师\n 别名: 无\n 待修正的错误写法: 无\n 身份/简介: 被邀请来为苏家拍摄全家福的专业人士。\n 核心特点: 专业,注重细节。\n 关系: 无。\n---------------------------------\n\n现在,请根据以上所有信息和提供的视频,为系列剧的 第 18 集 创作剧本。\n\n--- 上下文参考 ---\n前一集结尾内容:\n---\n无\n---\n\n后一集开头内容:\n---\n无\n---\n-------------------\n",
|
440
|
+
"type": "text"
|
441
|
+
},
|
442
|
+
{
|
443
|
+
"type": "video_url",
|
444
|
+
"video_url": {
|
445
|
+
"url": "https://lmdbk.com/5.mp4"
|
440
446
|
}
|
441
|
-
|
442
|
-
|
443
|
-
|
447
|
+
}
|
448
|
+
]
|
449
|
+
}
|
450
|
+
]
|
451
|
+
|
452
|
+
messages = [
|
453
|
+
|
454
|
+
{
|
455
|
+
"role": "user",
|
456
|
+
"content": [
|
457
|
+
{
|
458
|
+
"text": "画条狗",
|
459
|
+
"type": "text"
|
460
|
+
},
|
461
|
+
|
462
|
+
]
|
463
|
+
}
|
464
|
+
]
|
444
465
|
|
445
|
-
#
|
446
466
|
request = CompletionRequest(
|
447
467
|
# model="qwen-turbo-2024-11-01",
|
448
468
|
# model="gemini-all",
|
449
469
|
# model="gemini-2.0-flash-exp-image-generation",
|
450
|
-
model="gemini-2.0-flash",
|
470
|
+
# model="gemini-2.0-flash",
|
451
471
|
# model="gemini-2.5-flash-preview-04-17",
|
452
472
|
# model="gemini-2.5-flash-preview-04-17",
|
453
473
|
|
474
|
+
model="gemini-2.5-flash-image-preview",
|
475
|
+
|
454
476
|
# messages=[
|
455
477
|
# {
|
456
478
|
# 'role': 'user',
|
@@ -465,15 +487,37 @@ if __name__ == '__main__':
|
|
465
487
|
|
466
488
|
# arun(Completions(api_key=api_key).create_for_search(request))
|
467
489
|
|
468
|
-
|
469
|
-
|
490
|
+
base_url = "gemini/v1beta/models/{self.model}:generateContent"
|
491
|
+
|
492
|
+
base_url = "http://38.46.219.252:9001"
|
493
|
+
api_key = "sk-Azgp1thTIonR7IdIEqlJU51tpDYNIYYpxHvAZwFeJiOdVWiz"
|
494
|
+
|
495
|
+
base_url = "https://api.huandutech.com"
|
496
|
+
api_key = "sk-qOpbMHesasoVgX75ZoeEeBEf1R9dmsUZVAPcu5KkvLFhElrn"
|
497
|
+
|
498
|
+
# arun(Completions(base_url=base_url, api_key=api_key).create_for_images(request))
|
499
|
+
# arun(Completions(base_url=base_url, api_key=api_key).generate(request))
|
500
|
+
|
501
|
+
# arun(Completions().create_for_files(request))
|
470
502
|
|
471
503
|
# arun(Completions(api_key=api_key).create_for_files(request))
|
472
504
|
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
505
|
+
request = ImageRequest(
|
506
|
+
model="gemini-2.5-flash-image-preview",
|
507
|
+
|
508
|
+
# prompt="https://oss.ffire.cc/files/nsfw.jpg 移除右下角 白色的水印",
|
509
|
+
# prompt="画条可爱的狗",
|
510
|
+
|
511
|
+
# prompt="带个墨镜",
|
512
|
+
# image="https://oss.ffire.cc/files/kling_watermark.png",
|
513
|
+
|
514
|
+
prompt="把小鸭子放在女人的T恤上面",
|
515
|
+
|
516
|
+
image=[
|
517
|
+
"https://v3.fal.media/files/penguin/XoW0qavfF-ahg-jX4BMyL_image.webp",
|
518
|
+
"https://v3.fal.media/files/tiger/bml6YA7DWJXOigadvxk75_image.webp"
|
519
|
+
]
|
520
|
+
|
521
|
+
)
|
522
|
+
|
523
|
+
arun(Completions(base_url=base_url, api_key=api_key).generate(request))
|
meutils/apis/google/images.py
CHANGED
@@ -7,21 +7,113 @@
|
|
7
7
|
# @WeChat : meutils
|
8
8
|
# @Software : PyCharm
|
9
9
|
# @Description : D3 生图、编辑图
|
10
|
+
"""
|
11
|
+
# {
|
12
|
+
# "index": 0,
|
13
|
+
# "type": "image_url",
|
14
|
+
# "image_url": {
|
15
|
+
# "url": "b64"
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
# }
|
10
19
|
|
11
|
-
|
20
|
+
"""
|
12
21
|
|
13
|
-
from meutils.
|
22
|
+
from meutils.pipe import *
|
23
|
+
from meutils.io.files_utils import to_url
|
24
|
+
from meutils.llm.clients import AsyncOpenAI
|
25
|
+
from meutils.apis.images.edits import edit_image, ImageProcess
|
14
26
|
|
15
|
-
from meutils.schemas.image_types import ImageRequest
|
27
|
+
from meutils.schemas.image_types import ImageRequest, ImagesResponse
|
28
|
+
from meutils.schemas.openai_types import CompletionRequest
|
16
29
|
|
17
30
|
|
31
|
+
async def generate(request: ImageRequest, api_key: Optional[str] = None, base_url: Optional[str] = None):
|
32
|
+
is_hd = False
|
33
|
+
if request.model.endswith("-hd"):
|
34
|
+
is_hd = True
|
35
|
+
request.model = request.model.removesuffix("-hd")
|
18
36
|
|
37
|
+
image_urls = request.image_urls
|
38
|
+
image_urls = await to_url(image_urls, content_type="image/png")
|
39
|
+
image_urls = [
|
40
|
+
{
|
41
|
+
"type": "image_url",
|
42
|
+
"image_url": {
|
43
|
+
"url": image_url
|
44
|
+
}
|
45
|
+
}
|
46
|
+
for image_url in image_urls or []
|
47
|
+
]
|
19
48
|
|
20
|
-
async def generate(request: ImageRequest, api_key: Optional[str] = None):
|
21
49
|
request = CompletionRequest(
|
22
|
-
model=
|
50
|
+
model=request.model,
|
51
|
+
stream=False,
|
52
|
+
max_tokens=None,
|
23
53
|
messages=[
|
24
|
-
|
54
|
+
{
|
55
|
+
"role": "user",
|
56
|
+
"content": [
|
57
|
+
{
|
58
|
+
"type": "text",
|
59
|
+
"text": request.prompt
|
60
|
+
},
|
61
|
+
*image_urls
|
62
|
+
]
|
63
|
+
}
|
25
64
|
],
|
26
65
|
)
|
27
|
-
|
66
|
+
|
67
|
+
data = request.model_dump(exclude_none=True)
|
68
|
+
|
69
|
+
client = AsyncOpenAI(
|
70
|
+
base_url=base_url,
|
71
|
+
api_key=api_key
|
72
|
+
# base_url="https://openrouter.ai/api/v1",
|
73
|
+
# base_url="https://all.chatfire.cn/openrouter/v1",
|
74
|
+
# api_key=api_key or os.getenv("OPENROUTER_API_KEY"),
|
75
|
+
|
76
|
+
)
|
77
|
+
|
78
|
+
completion = await client.chat.completions.create(**data)
|
79
|
+
logger.debug(completion)
|
80
|
+
if (
|
81
|
+
completion
|
82
|
+
and completion.choices
|
83
|
+
and hasattr(completion.choices[0].message, "images")
|
84
|
+
and (images := completion.choices[0].message.images)
|
85
|
+
):
|
86
|
+
image_urls = [image['image_url']['url'] for image in images]
|
87
|
+
logger.debug(image_urls)
|
88
|
+
|
89
|
+
# if is_hd:
|
90
|
+
# tasks = [edit_image(ImageProcess(model="clarity", image=image_url)) for image_url in image_urls]
|
91
|
+
# responses = await asyncio.gather(*tasks)
|
92
|
+
#
|
93
|
+
# logger.debug(responses)
|
94
|
+
#
|
95
|
+
# image_urls = [response.data[0]["url"] for response in responses if response.data]
|
96
|
+
#
|
97
|
+
# else:
|
98
|
+
image_urls = await to_url(image_urls, content_type="image/png")
|
99
|
+
|
100
|
+
return ImagesResponse(image=image_urls)
|
101
|
+
|
102
|
+
|
103
|
+
if __name__ == '__main__':
|
104
|
+
base_url = "https://api.huandutech.com/v1"
|
105
|
+
api_key = "sk-qOpbMHesasoVgX75ZoeEeBEf1R9dmsUZVAPcu5KkvLFhElrn"
|
106
|
+
request = ImageRequest(
|
107
|
+
# model="google/gemini-2.5-flash-image-preview:free",
|
108
|
+
model="gemini-2.5-flash-image-preview",
|
109
|
+
# model="gemini-2.5-flash-image-preview-hd",
|
110
|
+
|
111
|
+
prompt="带个墨镜",
|
112
|
+
# image=["https://oss.ffire.cc/files/kling_watermark.png"],
|
113
|
+
)
|
114
|
+
|
115
|
+
r = arun(
|
116
|
+
generate(
|
117
|
+
request, base_url=base_url, api_key=api_key
|
118
|
+
)
|
119
|
+
)
|
meutils/apis/images/__init__.py
CHANGED
@@ -18,10 +18,13 @@ from meutils.apis.fal.images import generate as fal_generate
|
|
18
18
|
|
19
19
|
from meutils.apis.gitee.image_to_3d import generate as image_to_3d_generate
|
20
20
|
from meutils.apis.gitee.openai_images import generate as gitee_images_generate
|
21
|
-
from meutils.apis.qwen.chat import Completions as QwenCompletions
|
22
21
|
from meutils.apis.volcengine_apis.images import generate as volc_generate
|
23
22
|
from meutils.apis.images.recraft import generate as recraft_generate
|
24
23
|
from meutils.apis.jimeng.images import generate as jimeng_generate
|
24
|
+
# from meutils.apis.google.images import generate as google_generate
|
25
|
+
|
26
|
+
from meutils.apis.qwen.chat import Completions as QwenCompletions
|
27
|
+
from meutils.apis.google.chat import Completions as GoogleCompletions
|
25
28
|
|
26
29
|
|
27
30
|
async def generate(
|
@@ -29,20 +32,7 @@ async def generate(
|
|
29
32
|
api_key: Optional[str] = None,
|
30
33
|
base_url: Optional[str] = None,
|
31
34
|
):
|
32
|
-
|
33
|
-
data = {
|
34
|
-
**request.model_dump(exclude_none=True, exclude={"extra_fields", "aspect_ratio"}),
|
35
|
-
**(request.extra_fields or {})
|
36
|
-
}
|
37
|
-
request = ImageRequest(**data)
|
38
|
-
if request.model.startswith("doubao"):
|
39
|
-
request.watermark = False
|
40
|
-
if request.image and isinstance(request.image, list):
|
41
|
-
request.image = request.image[0]
|
42
|
-
|
43
|
-
data = to_openai_params(request)
|
44
|
-
client = AsyncClient(api_key=api_key, base_url=base_url)
|
45
|
-
return await client.images.generate(**data)
|
35
|
+
logger.debug(request)
|
46
36
|
|
47
37
|
if request.model.startswith("fal-ai"): # 主要 request.image
|
48
38
|
return await fal_generate(request, api_key)
|
@@ -51,12 +41,12 @@ async def generate(
|
|
51
41
|
request = RecraftImageRequest(**request.model_dump(exclude_none=True))
|
52
42
|
return await recraft_generate(request)
|
53
43
|
|
54
|
-
if request.model.startswith(("
|
55
|
-
return await jimeng_generate(request)
|
56
|
-
|
57
|
-
if request.model.startswith(("seed", "seededit_v3.0", "byteedit_v2.0")):
|
44
|
+
if request.model.startswith(("seed", "seededit_v3.0", "byteedit_v2.0", "i2i_portrait_photo")): # seededit seedream
|
58
45
|
return await volc_generate(request, api_key)
|
59
46
|
|
47
|
+
if request.model.startswith(("jimeng")): # 即梦
|
48
|
+
return await jimeng_generate(request)
|
49
|
+
|
60
50
|
if request.model in {"Hunyuan3D-2", "Hi3DGen", "Step1X-3D"}:
|
61
51
|
return await image_to_3d_generate(request, api_key)
|
62
52
|
|
@@ -68,9 +58,30 @@ async def generate(
|
|
68
58
|
request.image = request.image[-1]
|
69
59
|
return await QwenCompletions(api_key=api_key).generate(request)
|
70
60
|
|
61
|
+
if request.model.startswith(("google/gemini", "gemini")):
|
62
|
+
return await GoogleCompletions(base_url=base_url, api_key=api_key).generate(request)
|
63
|
+
|
64
|
+
# 其他
|
65
|
+
data = {
|
66
|
+
**request.model_dump(exclude_none=True, exclude={"extra_fields", "aspect_ratio"}),
|
67
|
+
**(request.extra_fields or {})
|
68
|
+
}
|
69
|
+
request = ImageRequest(**data)
|
70
|
+
if request.model.startswith("doubao"):
|
71
|
+
request.watermark = False
|
72
|
+
if request.image and isinstance(request.image, list):
|
73
|
+
request.image = request.image[0]
|
74
|
+
|
75
|
+
data = to_openai_params(request)
|
76
|
+
client = AsyncClient(api_key=api_key, base_url=base_url)
|
77
|
+
return await client.images.generate(**data)
|
78
|
+
|
71
79
|
|
72
80
|
# "flux.1-krea-dev"
|
73
81
|
|
74
82
|
if __name__ == '__main__':
|
75
83
|
# arun(generate(ImageRequest(model="flux", prompt="笑起来")))
|
76
|
-
arun(generate(ImageRequest(model="FLUX_1-Krea-dev", prompt="笑起来")))
|
84
|
+
# arun(generate(ImageRequest(model="FLUX_1-Krea-dev", prompt="笑起来")))
|
85
|
+
|
86
|
+
token = f"""{os.getenv("VOLC_ACCESSKEY")}|{os.getenv("VOLC_SECRETKEY")}"""
|
87
|
+
arun(generate(ImageRequest(model="seed", prompt="笑起来"), api_key=token))
|
@@ -42,7 +42,6 @@ async def generate(request: ImageRequest, token: Optional[str] = None):
|
|
42
42
|
"req_key": "high_aes_general_v30l_zt2i",
|
43
43
|
# "req_key": "jimeng_high_aes_general_v21_L",
|
44
44
|
|
45
|
-
"prompt": request.prompt,
|
46
45
|
"seed": request.seed,
|
47
46
|
"width": 1328,
|
48
47
|
"height": 1328,
|
@@ -53,17 +52,16 @@ async def generate(request: ImageRequest, token: Optional[str] = None):
|
|
53
52
|
# "use_rephraser": True
|
54
53
|
}
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
if image:
|
60
|
-
payload["image_urls"] = [image]
|
55
|
+
if request.image_urls:
|
56
|
+
payload["image_urls"] = request.image_urls
|
61
57
|
payload["req_key"] = request.model
|
62
58
|
if payload["req_key"] not in {"seededit_v3.0", "byteedit_v2.0"}:
|
63
59
|
payload["req_key"] = "byteedit_v2.0" # "seededit_v3.0" https://www.volcengine.com/docs/85128/1602254
|
64
60
|
|
61
|
+
width, height = map(int, request.size.split('x'))
|
65
62
|
payload['width'] = width
|
66
63
|
payload['height'] = height
|
64
|
+
payload['prompt'] = request.prompt
|
67
65
|
|
68
66
|
if request.response_format == "oss_url":
|
69
67
|
payload["return_url"] = False
|
@@ -91,21 +89,22 @@ async def generate(request: ImageRequest, token: Optional[str] = None):
|
|
91
89
|
|
92
90
|
if __name__ == '__main__':
|
93
91
|
token = f"""{os.getenv("VOLC_ACCESSKEY")}|{os.getenv("VOLC_SECRETKEY")}"""
|
94
|
-
token = "AKLTOWM5ZTc5ZDFhZWNlNDIzODkwYmZiNjEyNzYwNzE0MTI|T0RCbFpHRTJaRFEyWmpjeE5ERXpNR0ptWlRCaU16WmhPRE0wWVdKa01tTQ=="
|
95
92
|
prompt = """
|
96
93
|
3D魔童哪吒 c4d 搬砖 很开心, 很快乐, 精神抖擞, 背景是数不清的敖丙虚化 视觉冲击力强 大师构图 色彩鲜艳丰富 吸引人 背景用黄金色艺术字写着“搬砖挣钱” 冷暖色对比
|
97
94
|
"""
|
98
95
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
96
|
+
prompt = """
|
97
|
+
|
98
|
+
让这个女人带上眼镜 衣服换个颜色
|
99
|
+
"""
|
103
100
|
|
104
101
|
request = ImageRequest(
|
105
|
-
model="high_aes_general_v30l_zt2i",
|
102
|
+
# model="high_aes_general_v30l_zt2i",
|
103
|
+
model="seededit_v3.0",
|
106
104
|
prompt=prompt,
|
107
105
|
response_format="url",
|
108
106
|
size="512x1328",
|
107
|
+
# image="https://oss.ffire.cc/files/kling_watermark.png"
|
109
108
|
)
|
110
109
|
|
111
110
|
arun(generate(request, token=token))
|
meutils/data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2025.08.
|
1
|
+
2025.08.29.15.13.52
|